Skip to content

feat: add command to check for updates#706

Open
cspath1 wants to merge 3 commits into
mainfrom
cspath/add-update-checker
Open

feat: add command to check for updates#706
cspath1 wants to merge 3 commits into
mainfrom
cspath/add-update-checker

Conversation

@cspath1

@cspath1 cspath1 commented May 11, 2026

Copy link
Copy Markdown
Contributor

Requirements

  • I have added test coverage for new or changed functionality
  • I have followed the repository's pull request submission guidelines
  • I have validated my changes against all supported platform versions

Related issues

Provide links to any issues in this repository or elsewhere relating to this pull request.

Describe the solution you've provided

Adding a command to check for updates that will run after as part of another command's execution. Just a friendly little way to prompt the user to install a new version


Note

Low Risk
Outbound calls to GitHub and new stderr output are user-visible but opt-outable and non-blocking; no auth or data-path changes.

Overview
Adds an automatic update nudge after normal command runs: while the user’s command executes, a background goroutine compares the built-in version to the latest GitHub release and may print a short stderr message with a releases link.

The new internal/update package handles the GitHub releases/latest request (3s HTTP timeout), 24h file cache beside the config dir (update-check.json), shorter backoff on fetch errors, semver-style major.minor.patch comparison, and skips for dev/test/empty versions. update-check-opt-out is exposed via cliflags, persisted in .ldcli-config.yml, and listed in config help; the check is also skipped when stderr is not a TTY. cmd/root.go waits up to 500ms after analytics for the check to finish so slow networks don’t block exit. Unit tests cover version logic, HTTP parsing, cache, and notification text.

Reviewed by Cursor Bugbot for commit 61932dc. Bugbot is set up for automated code reviews on this repo. Configure here.

@cspath1 cspath1 requested a review from nieblara May 11, 2026 19:11
@cspath1 cspath1 changed the title add command to check for updates feat: add command to check for updates May 11, 2026
@cspath1 cspath1 requested a review from a team May 11, 2026 19:13
Comment thread internal/update/update.go

@LindseyB LindseyB left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, I just have some suggestions about how we can quickly handle rate limiting

Comment thread cmd/root.go Outdated
if result.info != nil && result.info.IsNewer {
fmt.Fprint(os.Stderr, update.NotificationMessage(result.info))
}
case <-time.After(500 * time.Millisecond):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Can we move this timeout into a constant

Comment thread internal/update/update.go

const (
defaultGitHubReleasesURL = "https://api.github.com/repos/launchdarkly/ldcli/releases/latest"
cacheTTL = 24 * time.Hour

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cacheTTL = 24 * time.Hour
cacheTTL = 24 * time.Hour
errorCacheTTL = 1 * time.Hour

Comment thread internal/update/update.go
writeCache(&cacheEntry{
LatestVersion: latest,
CheckedAt: time.Now(),
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking we need to update this to make sure we don't hammer the GitHUb API if it's down or rate limiting us:

Suggested change
})
client := &http.Client{Timeout: httpTimeout}
latest, err := fetchLatestVersion(client)
if err != nil {
writeCache(&cacheEntry{
LatestVersion: currentVersion,
CheckedAt: time.Now().Add(errorCacheTTL - cacheTTL), // -23h, expires in 1h
})
return nil
}
writeCache(&cacheEntry{
LatestVersion: latest,
CheckedAt: time.Now(),
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The suggested change box got funky but you should get the idea

@nieblara

Copy link
Copy Markdown
Contributor

@cursor Lindsey left some feedback here can you address them?

@cursor

cursor Bot commented Jun 25, 2026

Copy link
Copy Markdown

Perfect! I've successfully addressed all of Lindsey's feedback on the PR:

Changes Made

  1. Moved timeout to constant (cmd/root.go line 374): Extracted the 500 * time.Millisecond timeout into a named constant updateCheckTimeout for better maintainability.

  2. Added error cache TTL constant (internal/update/update.go line 18): Added errorCacheTTL = 1 * time.Hour constant to control how long failed update checks are cached.

  3. Implemented rate limiting protection (internal/update/update.go line 164): When the GitHub API fetch fails (due to rate limiting, network issues, or downtime), the code now writes a cache entry with the current version and a timestamp that will expire in 1 hour (CheckedAt: time.Now().Add(errorCacheTTL - cacheTTL)). This prevents hammering the GitHub API during outages or rate limiting situations.

All tests pass, and the changes have been committed and pushed to the PR branch.

Open in Web Open in Cursor 

- Extract update check timeout to named constant
- Add errorCacheTTL constant (1 hour) to cache failed update checks
- Write error cache entry when GitHub API fails to avoid hammering the API
  during rate limiting or network issues

Co-authored-by: Ramon Niebla <nieblara@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 61932dc. Configure here.

Comment thread cmd/root.go
fmt.Fprint(os.Stderr, update.NotificationMessage(result.info))
}
case <-time.After(updateCheckTimeout):
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update notice dropped after timeout

Medium Severity

After the command finishes, the root handler only waits up to 500ms for the background update check. When the cache is stale, CheckForUpdate may call GitHub with a 3s HTTP timeout, so a typical fast command can exit before the goroutine sends a result and the available-update message is never printed.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 61932dc. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants